import cv2 as cv
import copy
import numpy as np
from matplotlib import pyplot as plt

# 第二周进阶作业

filename = r'D:\Lena.png'
img = cv.imread(filename)

# 显示原图片
cv.imshow("Hello,world!",img)

# 1. 以Lena为原始图像，通过OpenCV实现平均滤波，高斯滤波及中值滤波，比较滤波结果。

# 均值滤波
blurImg = cv.blur(img,(5, 5))

# 高斯滤波
gaussianImg = cv.GaussianBlur(img, (5, 5), 0)

# 中值滤波
medianImg = cv.medianBlur(img, 5)

# 平均滤波对噪声的效果差，而中值滤波和高斯滤波对噪声效果好
# 高斯平滑在图像平滑处理中表现较好，也是非常常用的一种滤波方式
# 中值滤波有利于椒盐噪声的处理，还有一定的保边作用
cv.imshow("blur", blurImg)
cv.imshow("GaussianBlur", gaussianImg)
cv.imshow("medianBlur", medianImg)

# 2. 以Lena为原始图像，通过OpenCV使用Sobel及Canny算子检测，比较边缘检测结果。

# Sobel算子
cv.imshow("Sobel", cv.Sobel(img, cv.CV_8U, 1, 1))
# Canny算子的基本优点在于检测准确、对噪声稳健，在实际中广泛应用
cv.imshow("Canny", cv.Canny(img, 50, 150))


# 3. 首先计算灰度直方图，进一步使用大津算法进行分割，并比较分析分割结果。

# 转换灰度图像
pic6 = cv.imread(r'D:\opencv\sources\samples\data\pic6.png')
grayPic = cv.cvtColor(pic6, cv.COLOR_BGR2GRAY)
cv.imshow("pic6", pic6)
#计算灰度直方图
histPic = cv.calcHist([grayPic], [0], None, [256], [0, 256])
#绘制灰度直方图
plt.plot(histPic)
#图像参数，绘制x轴的长度
plt.xlim([0, 256])
plt.title("pic6_hist")
plt.show()

# 大津算法分割

# 高斯滤波
gaussGrayPic = cv.GaussianBlur(grayPic, (5, 5), 0)
# 大津算法阈值分割
_,grayOTSUPic = cv.threshold(gaussGrayPic, 125, 255, cv.THRESH_BINARY)
# 数学形态学滤波
element = cv.getStructuringElement(cv.MORPH_CROSS, (3,3))
# 开运算滤波
OTSUPic = cv.morphologyEx(grayOTSUPic, cv.MORPH_OPEN, element)
# 渐变色度的图像使用大津算法没有很好的效果
cv.imshow("OTSUPic", grayOTSUPic)

# 4. 使用米粒图像，分割得到各米粒，首先计算各区域(米粒)的面积、长度等信息，
# 进一步计算面积、长度的均值及方差，分析落在3sigma范围内米粒的数量。
riceImg = cv.imread(r'D:\rice.png')
grayRiceImg = cv.cvtColor(riceImg, cv.COLOR_BGR2GRAY)
cv.imshow("riceImg", riceImg)
# 使用大津算法、形态学以及开运算去辅助处理图像

# 使用全局大津算法，总数量只有90个，使用局部大津算法，总数量是93个,形态学及开运算辅助处理后为95个
# _,dstRice = cv.threshold(grayRiceImg, 125, 255, cv.THRESH_BINARY)
dstRice = cv.adaptiveThreshold(grayRiceImg, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 103, 1)
# 数学形态学滤波
elementRice = cv.getStructuringElement(cv.MORPH_CROSS, (3,3))
# 开运算滤波
morphRice = cv.morphologyEx(dstRice, cv.MORPH_OPEN, elementRice)
cv.imshow("OTSURice", dstRice)
# 检测米粒轮廓
seg = copy.deepcopy(morphRice)
bin, cnts, hier = cv.findContours(seg, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 分析目标
count = 0
# 米粒面积数组
areaArray = np.array([])
# 米粒长度数组
lineArray = np.array([])
for i in range(len(cnts), 0, -1):
    cnt = cnts[i - 1]
    area = cv.contourArea(cnt)  # 米粒面积
    if area < 10:
        continue
    count = count + 1
    areaArray = np.append(areaArray, area)
    # 精确米粒边界
    minAreaRect = cv.minAreaRect(cnt)
    # 得到米粒的4个坐标
    x, y, z, p = cv.boxPoints(minAreaRect)
    # 计算米粒的长度
    l1 = ((x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2) ** 0.5
    l2 = ((x[0] - p[0]) ** 2 + (x[1] - p[1]) ** 2) ** 0.5
    if l1 > l2:
        line = l1
    else:
        line = l2
    print("米粒", i, "面积:", area, "长度:", round(line, 2))
    lineArray = np.append(lineArray, line)
    cv.line(riceImg, tuple(x), tuple(y), (255, 0, 0), 1)
    cv.line(riceImg, tuple(y), tuple(z), (255, 0, 0), 1)
    cv.line(riceImg, tuple(z), tuple(p), (255, 0, 0), 1)
    cv.line(riceImg, tuple(p), tuple(x), (255, 0, 0), 1)
    cv.putText(riceImg, str(count), (x[0], x[1]), cv.FONT_HERSHEY_PLAIN, 0.5, (0, 0xff, 0))
print("------------------------------")
print("米粒总数量: ", len(areaArray))
print("------------------------------")
areaAll = areaArray.sum()
print("总面积:", round(areaAll, 3))
averageArea = areaAll / count
print("平均面积:", round(averageArea, 3))
std = areaArray.std()
print("面积标准差:", round(std, 3))
area1 = averageArea - std * 1.5
area2 = averageArea + std * 1.5
print("面积3sigma的取值范围:", round(area1, 3), "--", round(area2, 3))
count1 = 0
for i in areaArray:
    if i > area1 and i < area2:
        count1 = count1 + 1
print("米粒面积在3sigma内的数量为:", count1)
print("------------------------------")
line_all = lineArray.sum()
print("总长度:", round(line_all, 3))
average_line = line_all / count
print("平均长度:", round(average_line, 3))
std_line = lineArray.std()
print("长度标准差:", round(std_line, 3))
line1 = average_line - std_line * 1.5
line2 = average_line + std_line * 1.5
print("长度3sigma的取值范围:", round(line1, 3), "--", round(line2, 3))
count2 = 0
for i in lineArray:
    if line2 > i > line1:
        count2 = count2 + 1
print("米粒长度在3sigma内的数量为:", count2)
print("------------------------------")
cv.imshow("fengetuRice", riceImg)

# 5. 使用棋盘格及自选风景图像，分别使用SIFT、FAST及ORB算子检测角点，并比较分析检测结果。
# (可选)使用Harris角点检测算子检测棋盘格，并与上述结果比较。

# SIFT
boxL = cv.imread(r'D:\opencv\sources\samples\data\box_in_scene.png')
boxR = cv.imread(r'D:\opencv\sources\samples\data\box.png')
# 转换为灰度图
boxGrayL = cv.cvtColor(boxL, cv.COLOR_BGR2GRAY)
boxGrayR = cv.cvtColor(boxR, cv.COLOR_BGR2GRAY)
# 检测提取描述子
sift = cv.xfeatures2d_SIFT.create()
box_kL, box_dL = sift.detectAndCompute(boxGrayL, None)
box_kR, box_dR = sift.detectAndCompute(boxGrayR, None)
# 画出匹配点
bf = cv.BFMatcher(cv.NORM_L2)
matches = bf.match(box_dL, box_dR)
boxImg = cv.drawMatches(boxL, box_kL, boxR, box_kR, matches, None, flags=2)
cv.imshow("SIFT", boxImg)

# FAST
home = cv.imread(r'D:\opencv\sources\samples\data\home.jpg')
# 转换为灰度图
grayHome = cv.cvtColor(home, cv.COLOR_BGR2GRAY)
# 提取特征点
fast = cv.FastFeatureDetector_create(50)
# 检测
homeDet = fast.detect(grayHome, None)
# 绘制
homeDraw = cv.drawKeypoints(home, homeDet, home, color=(255, 255, 255))
cv.imshow("FAST", homeDraw)


# ORB
# 读入左右反向图片
bookL = cv.imread(r'D:\opencv\sources\samples\data\left.jpg')
bookR = cv.imread(r'D:\opencv\sources\samples\data\right.jpg')
# 灰度图转换
bookGrayL = cv.cvtColor(bookL, cv.COLOR_BGR2GRAY)
bookGrayR = cv.cvtColor(bookR, cv.COLOR_BGR2GRAY)
# 创建orb算子以及提取描述子
orb = cv.ORB_create()
book_kL, book_dL = orb.detectAndCompute(bookGrayL, None)
book_kR, book_dR = orb.detectAndCompute(bookGrayR, None)
# 匹配
matches = bf.match(book_dL, book_dR)
bookImg = cv.drawMatches(bookL, book_kL, bookR, book_kR, matches, None, flags=2)
cv.imshow("ORB", bookImg)

cv.waitKey()
cv.destroyAllWindows()

